Write a custom CUDA kernel to optimize `Pserf` (Parametric Serf).

Formula: f(x) = x * erf(gamma * log(1 + exp(delta * x)))

Problem Analysis:
1. Computationally Intensive & Memory Bound: The operation is element-wise but involves a long chain of expensive transcendental functions (exp, log, erf).
2. Operator Chaining: A standard PyTorch implementation creates multiple intermediate tensors.

Optimization Strategy: Fused Element-wise Kernel with Vectorization

1. One-Thread-per-Element: Map each element to a CUDA thread.

2. Vectorized Loads (float4): Use `float4` to process 128 bits per memory transaction.

3. Fused Stable Math:
   - For each element `x`:
     `dx = delta * x`
     `sp = (dx > 20) ? dx : log1pf(__expf(dx))` (Stable Softplus)
     `inner_val = gamma * sp`
     `erf_val = erff(inner_val)`
     `result = x * erf_val`
   - All steps are fused in registers.

4. One-Pass: Fuse all logic into a single read-compute-write kernel.
  
Here's an example to show you the syntax of inline embedding custom CUDA operators in torch: The example given architecture is:   
  
```python
import torch
import torch.nn as nn
import torch.nn.functional as F

BATCH_SIZE = 4096
HIDDEN_DIM = 4096
SHAPE = (BATCH_SIZE, HIDDEN_DIM)

GAMMA_VAL = 1.25
DELTA_VAL = 0.85

class Pserf(nn.Module):
    '''
    "ErfAct and Pserf: Non-monotonic Smooth Trainable Activation Functions" (AAAI 2022)
    https://doi.org/10.1609/aaai.v36i6.20557
    Formula: f(x) = x * erf(gamma * log(1 + exp(delta * x)))
    '''
    def __init__(self, gamma=1.25, delta=0.85):
        super(Pserf, self).__init__()
        self.gamma = gamma
        self.delta = delta

    def forward(self, x: torch.Tensor) -> torch.Tensor:
        sp = F.softplus(self.delta * x)
        erf_val = torch.erf(self.gamma * sp)
        return x * erf_val

class Model(nn.Module):
    def __init__(self, gamma=1.25, delta=0.85):
        super(Model, self).__init__()
        self.act = Pserf(gamma, delta)
    
    def forward(self, x):
        return self.act(x)

def get_inputs():
    input_tensor = torch.randn(SHAPE, dtype=torch.float32) * 5.0
    return [input_tensor.contiguous()]

def get_init_inputs():
    return [GAMMA_VAL, DELTA_VAL]